package ru.spbau.lifeincube.swing;

import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import ru.spbau.lifeincube.logic.Cell;
import ru.spbau.lifeincube.logic.Field;
import ru.spbau.lifeincube.logic.Game;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class Controller implements FrameActionListener {
    private Game mGame;
    private Timer mTimer;
    private ObservableList<Cell> mAddedCells;
    private ObservableList<Cell> mRemovedCells;

    public Controller(int sizeX, int sizeY, int sizeZ, int liveLeft, int liveRight, int liveNeed) {
        Field field = new Field(sizeX, sizeY, sizeZ);
        mGame = new Game(field, liveLeft, liveRight, liveNeed);

        //TODO: setting random initial state. Should be replaced with loading from file.
        int[][][] initialState = generateHardcodedState(sizeX, sizeY, sizeZ);
        mGame.setInitialState(initialState);

        mTimer = new Timer(1000, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                makeMove();
            }
        });

        mAddedCells = FXCollections.observableList(new ArrayList<Cell>());
        mRemovedCells = FXCollections.observableList(new ArrayList<Cell>());

	    Platform.runLater(new Runnable() {
		    @Override
		    public void run() {
			    mAddedCells.setAll(mGame.addedCells());
		    }
	    });
    }

	private int[][][] generateHardcodedState(int sizeX, int sizeY, int sizeZ) {
		int[][][] initialState = new int[sizeX][sizeY][sizeZ];
		for (int i = 0; i < sizeX; i++) {
			for (int j = 0; j < sizeY; j++)
				for (int k = 0; k < sizeZ; k++)
					initialState[i][j][k] = 0;
		}
		initialState[sizeX/2][sizeY/2][sizeZ/2] = 1;
		initialState[sizeX/2-1][sizeY/2-1][sizeZ/2] = 1;
		initialState[sizeX/2-1][sizeY/2][sizeZ/2] = 1;
		initialState[sizeX/2][sizeY/2-1][sizeZ/2] = 1;
		initialState[sizeX/2][sizeY/2][sizeZ/2+1] = 1;
		return initialState;
	}

	private int[][][] generateRandomState(int sizeX, int sizeY, int sizeZ) {
        Random random = new Random();
        int[][][] initialState = new int[sizeX][sizeY][sizeZ];
        for (int i = 0; i < sizeX; i++) {
            for (int j = 0; j < sizeY; j++)
                for (int k = 0; k < sizeZ; k++)
                    initialState[i][j][k] = random.nextInt(2);
        }
        return initialState;
    }

    private void makeMove() {
        if (!mGame.makeMove()) return;
        final List<Cell> added = mGame.addedCells();
        final List<Cell> removed = mGame.removedCells();
        System.out.println(String.format("Added in controller : %d", added.size()));
        System.out.println(String.format("Removed in controller : %d", removed.size()));

        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                mAddedCells.setAll(added);
                mRemovedCells.setAll(removed);
            }
        });
    }

    private void moveToPrev() {
        if (mGame.getStatesCount() == 0) return;
        final List<Cell> added = mGame.removedCells();
        final List<Cell> removed = mGame.addedCells();
        mGame.moveToPrev();

        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                mAddedCells.setAll(added);
                mRemovedCells.setAll(removed);
            }
        });
    }

    public ObservableList<Cell> addedCells() {
        return mAddedCells;
    }

    public ObservableList<Cell> removedCells() {
        return mRemovedCells;
    }

    @Override
    public void onPlay() {
        mTimer.start();
    }

    @Override
    public void onPrevClicked() {
        mTimer.stop();
        moveToPrev();
    }

    @Override
    public void onNextClicked() {
        mTimer.stop();
        makeMove();
    }

    @Override
    public void onPause() {
        mTimer.stop();
    }

    @Override
    public void openFile(File f) {

    }

	public int getStep() {
		return mGame.getStatesCount();
	}

	public void refresh(int xValue, int yValue, int zValue, int toLiveValue, int toDieValue, int toBornValue) {
		Field field = new Field(xValue, yValue, zValue);
		mGame = new Game(field, toLiveValue, toDieValue, toBornValue);
		mAddedCells.clear();
		mRemovedCells.clear();
		mTimer.stop();
		int[][][] initialState = generateHardcodedState(xValue, yValue, zValue);
        mGame.setInitialState(initialState);
	}
}
